package sample.websocket.servlet;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.annotation.WebServlet;

import net.arnx.jsonic.JSON;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

@WebServlet(urlPatterns = { "/chat/" })
public class SampleVideoChatServlet extends WebSocketServlet {

	private Log log = LogFactory.getLog(getClass());

	private List<SampleStreamInbound> connections = new CopyOnWriteArrayList<>();

	private AtomicInteger count = new AtomicInteger(0);

	/**
	 * Version UID.
	 */
	private static final long serialVersionUID = 7695407012706356033L;

	@Override
	protected StreamInbound createWebSocketInbound(String arg0) {
		return new SampleStreamInbound(count.incrementAndGet());
	}

	private class SampleStreamInbound extends MessageInbound {

		private String name;

		public SampleStreamInbound(int idx) {
			this.name = String.format("No.%02d", idx);
			log.info(String.format("%s initialized.", this.name));
		}

		/**
		 * コネクション開始のメソッド
		 */
		protected void onOpen(WsOutbound outbound) {
			connections.add(this);
			log.info(String.format("%s opened.", name));
		}

		/**
		 * コネクション終了のメソッド
		 */
		@Override
		protected void onClose(int status) {
			connections.remove(this);
			log.info(String.format("%s closed.", name));
		}

		private void broadcast(String message) {
			for (SampleStreamInbound connection : connections) {
				try {

					ByteBuffer bBuffer = ByteBuffer.wrap(null);

					connection.getWsOutbound().writeBinaryMessage(bBuffer);
				} catch (IOException e) {
					log.error(e, e);
				}
			}
		}

		private void broadcast(ByteBuffer buffer) {
			for (SampleStreamInbound connection : connections) {
				try {
					connection.getWsOutbound().writeBinaryMessage(buffer);
				} catch (IOException e) {
					log.error(e, e);
				}
			}
		}

		@Override
		protected void onBinaryMessage(ByteBuffer message) throws IOException {
			log.info("onBinaryMessage called.");
			broadcast(message);
		}

		@Override
		protected void onTextMessage(CharBuffer message) throws IOException {
			log.info("onTextMessage called.");
			Message m = JSON.decode(message.toString(), Message.class);
			String filteredMessage = String.format("%s: %s", name, m.data);

			log.info(filteredMessage);
			broadcast(filteredMessage);
		}
	}

	public class Message {
		public Message() {
		}

		public Message(String message) {
			this.type = "message";
			this.data = message;
		}

		public String data;
		public String type;
	}
}
